/*
 * ScenariosPanel.java
 *
 * Created on September 27, 2007, 7:53 PM
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.apps.rtemaster.gui;

import java.awt.event.ActionEvent;
import org.fpdev.util.gui.GUIFactory;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.border.BevelBorder;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.fpdev.core.Scenario;
import org.fpdev.core.Scenarios;
import org.fpdev.apps.rtemaster.RouteMaster;
import org.fpdev.apps.rtemaster.gui.map.MapLayer;
import org.fpdev.apps.rtemaster.gui.map.MapLayers;

/**
 *
 * @author demory
 */
public class ScenariosPanel extends JPanel implements ActionListener {

  private RouteMaster av_;
  private JTree tree_;
  private JPanel treePanel_;
  private Map<String, JCheckBox> cbLookup_;

  private JComboBox activeScen_;
  private JCheckBox autoAdjust_;
  private List<Scenario> scenList_;

  /** Creates a new instance of ScenariosPanel */
  public ScenariosPanel(Scenarios scenarios, RouteMaster av, RMGUI gui) {
    av_ = av;
    setLayout(new BorderLayout());

    // active scenario chooser
    JPanel activeScenPanel = GUIFactory.newColumnPanel();
    activeScen_ = GUIFactory.newComboBox(0);
    activeScen_.setAlignmentX(LEFT_ALIGNMENT);
    activeScen_.addActionListener(this);
    scenList_ = new ArrayList<Scenario>();
    autoAdjust_ = GUIFactory.newCheckBox("Auto-adjust visibility", true);
    autoAdjust_.setAlignmentX(LEFT_ALIGNMENT);
    activeScenPanel.add(activeScen_);
    activeScenPanel.add(autoAdjust_);
    initActiveScenarioChooser();
    activeScenPanel.setBorder(new CompoundBorder(new TitledBorder("Active Scenario"), new EmptyBorder(0, 4, 0, 4)));

    // scenario tree

    treePanel_ = new JPanel(new GridLayout(1,1));

    add(activeScenPanel, BorderLayout.NORTH);
    add(treePanel_, BorderLayout.CENTER);

    initTree(gui);
      
    this.setBorder(new EmptyBorder(3,3,3,3));
      
  }

  public void actionPerformed(ActionEvent e) {
    // Active Scenario Drop-down
    if (e.getSource() == activeScen_ && av_.getGUI() != null && activeScen_.getSelectedIndex() != -1 && !scenList_.isEmpty()) {
      av_.setActiveScenario(scenList_.get(activeScen_.getSelectedIndex()));
      av_.getGUI().activeScenarioChanged();
      // update tree
      if(tree_ != null && autoAdjust_.isSelected()) {
        collapseTree();
        TreeModel model = tree_.getModel();
        recurseTree(model, model.getRoot());
      }
    }
  }

  public void updateScenarios() {
    tree_ = null;
    treePanel_.removeAll();
    initActiveScenarioChooser();
    initTree(av_.getGUI());
  }

  private void recurseTree(TreeModel model, Object node) {
    if(!model.isLeaf(node)) {
      BranchNode branch = (BranchNode) node;
      //System.out.println("branch scen="+branch.scen_.getName());
      if(branch.scen_ == av_.getActiveScenario()) {
        //System.out.println(" - active");
        tree_.expandPath(treePathToNode(branch));
        branch.setChildrenSelected(true, false);
      }
      else if(!branch.scen_.isAccessibleTo(av_.getActiveScenario())) {
        //System.out.println(" - not accessible");
        branch.setChildrenSelected(false, false);
      }
      else if(branch.scen_.isAccessibleTo(av_.getActiveScenario())) {
        //System.out.println(" - accessible");
        branch.setChildrenSelected(true, false);
      }
    }
    for(int i=0; i < model.getChildCount(node); i++) {
      Object child = model.getChild(node, i);
      recurseTree(model, child);
    }
  }

  private void collapseTree() {
    for(int r = tree_.getRowCount()-1; r >=0; r--) {
      tree_.collapseRow(r);
    }
  }

  private TreePath treePathToNode(DefaultMutableTreeNode node) {
    return new TreePath(((DefaultTreeModel) tree_.getModel()).getPathToRoot(node));
  }

  private void initActiveScenarioChooser() {
    activeScen_.removeAllItems();
    scenList_.clear();
    Iterator<Scenario> scenarios = av_.getEngine().getScenarios().getScenarios();
    while (scenarios.hasNext()) {
      Scenario s = scenarios.next();
      activeScen_.addItem(s.getName());
      scenList_.add(s);
    }
  }

  private void initTree(RMGUI gui) {
    cbLookup_ = new HashMap<String, JCheckBox>();
    tree_ = new JTree(getBranch(av_.getEngine().getScenarios().getBase(), gui.getMapPanel().getLayers()));
    tree_.setCellRenderer(new CBTreeCellRenderer());
    tree_.addMouseListener(new TreeMouseListener(tree_));
    //tree_.setEditable(false);
    tree_.setBorder(new CompoundBorder(new BevelBorder(BevelBorder.LOWERED), new EmptyBorder(4, 4, 4, 4)));

    treePanel_.add(new JScrollPane(tree_));

    tree_.addMouseListener(new MouseAdapter() {

      @Override
      public void mousePressed(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) {
          Object obj = tree_.getPathForLocation(e.getX(), e.getY()).getLastPathComponent();
          if(obj instanceof BranchNode) {
            BranchNode node = (BranchNode) obj;
            node.getMenu().show(tree_, e.getX(), e.getY());
          }
        }
      }
    });
  }

  /*public Scenario getActiveScenario() {
    return scenList_.get(activeScen_.getSelectedIndex());
  }*/

  public boolean isSelected(int scenID, int layerID) {
    return cbLookup_.get(scenID + "-" + layerID).isSelected();
  }

  private DefaultMutableTreeNode getBranch(Scenario s, MapLayers layers) {
    //DefaultMutableTreeNode node = new DefaultMutableTreeNode(s.getName());
    BranchNode node = new BranchNode(s);
    Iterator<MapLayer> lIter = layers.getLayers();
    while (lIter.hasNext()) {
      //node.add(new DefaultMutableTreeNode(lIter.next().getName()));
      MapLayer layer = lIter.next();
      LeafNode cbNode = new LeafNode(layer.getName(), s.getID() == 1);
      node.add(cbNode);
      //System.out.println(s.getID()+"-"+layer.getID());
      cbLookup_.put(s.getID() + "-" + layer.getID(), cbNode.getCheckBox());
    }

    Iterator<Scenario> children = s.getChildren();
    while (children.hasNext()) {
      node.add(getBranch(children.next(), layers));
    }

    //node.add(new DefaultMutableTreeNode("Walkable Streets"));
    //node.add(new DefaultMutableTreeNode("Non-Walkable Streets"));

    return node;
  }

  private DefaultMutableTreeNode getLeaf(Scenario s, MapLayer layer) {
    DefaultMutableTreeNode node = new DefaultMutableTreeNode(layer.getName());//new JCheckBox(layer.getName()));    
    return node;
  }

  // inner classes
  
  private class BranchNode extends DefaultMutableTreeNode implements ActionListener {
    
    private Scenario scen_;
    private JPopupMenu menu_;
    private JMenuItem moveElementsMI_, showAllMI_, hideAllMI_;
    
    private BranchNode(Scenario s) {
      super(s.getName());
      scen_ = s;
      menu_ = new JPopupMenu();
      moveElementsMI_ = newPopupMenuItem(menu_, "Move Selected Network Elements Here");
      showAllMI_ = newPopupMenuItem(menu_, "Show All Layers");
      hideAllMI_ = newPopupMenuItem(menu_, "Hide All Layers");
     
    }
    
    public JPopupMenu getMenu() {
      return menu_;
    } 
    
    private JMenuItem newPopupMenuItem(JPopupMenu menu, String text) {
      JMenuItem item = new JMenuItem(text);
      item.addActionListener(this);
      menu.add(item);
      return item;
    }

    public void actionPerformed(ActionEvent e) {
      if(e.getSource() == moveElementsMI_) av_.getNetworkOps().moveElementsToScenario(scen_);
      if(e.getSource() == showAllMI_) setChildrenSelected(true, true);
      if(e.getSource() == hideAllMI_) setChildrenSelected(false, true);
    }
    
    private void setChildrenSelected(boolean sel, boolean refresh) {
      for (Object obj : children) {
        if(obj instanceof LeafNode) {
          LeafNode node = (LeafNode) obj;
          node.getCheckBox().setSelected(sel);
        }
      }
      av_.getGUI().getMapPanel().cancelBuffer();
      tree_.repaint();
      if(refresh) av_.getGUI().getMapPanel().refresh(false, true, false);
    }
  }
  
  private class LeafNode extends DefaultMutableTreeNode {

    private JCheckBox cb_;

    public LeafNode(String label, boolean checked) {
      cb_ = GUIFactory.newCheckBox(label);
      //cb_.setEnabled(true);
      cb_.setSelected(checked);
      cb_.setBackground(Color.WHITE);
    }

    public JCheckBox getCheckBox() {
      return cb_;
    }
  }

  private class CBTreeCellRenderer extends DefaultTreeCellRenderer {

    public CBTreeCellRenderer() {
      super();
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
      if (leaf) {
        /*JCheckBox cb = GUIFactory.newCheckBox(value.toString());
        cb.setEnabled(true);
        cb.setBackground(Color.WHITE);
        return cb;*/

        LeafNode node = (LeafNode) value; //tree.getPathForRow(row).getLastPathComponent();
        return node.getCheckBox();
      }
      return super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
    }
  }

  private class TreeMouseListener extends MouseAdapter {

    private JTree tree_;
    int hotspot = new JCheckBox().getPreferredSize().width;

    public TreeMouseListener(JTree tree) {
      tree_ = tree;
    }

    @Override
    public void mouseClicked(MouseEvent me) {
      TreePath path = tree_.getPathForLocation(me.getX(), me.getY());
      if (path != null) {
        TreeNode node = (TreeNode) path.getLastPathComponent();
        if (node.isLeaf()) {
          JCheckBox cb = ((LeafNode) node).getCheckBox();
          cb.setSelected(!cb.isSelected());
          av_.getGUI().getMapPanel().cancelBuffer();
          tree_.repaint();
          av_.getGUI().getMapPanel().refresh(false, true, false);
        //System.out.println("tree mClick "+node.getParent().getIndex(node));
        }
      }
    }
  }
}
